
// Copyright (C) 2011 Luca Piccioni
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#define _USE_MATH_DEFINES

#include <cfloat>
#include <cstdio>
#include <cmath>
#include <ctime>
#include <exception>
#include <memory>

#include "Derm.Simd.h"

using namespace std;

void TestMemoryCorrectness();
void TestMemoryPerformances();

void TestMatrix4x4Correctness();
void TestMatrix4x4Performances();

int main(int argc, char *argv[])
{
	// Test operations validity
	TestMemoryCorrectness();
	TestMatrix4x4Correctness();
	// Test performances
	TestMemoryPerformances();
	TestMatrix4x4Performances();

	getchar();    

	return (0);
}

// Memory

void TestMemoryCorrectness()
{
	
}

void TestMemoryPerformances()
{
	const unsigned long long BYTES_COUNT = 1024 * 1024 * 64;

	unsigned char *src = (unsigned char*)_aligned_malloc(BYTES_COUNT, 16);
	unsigned char *dst = (unsigned char*)_aligned_malloc(BYTES_COUNT, 16);
	clock_t crono;

	crono = clock();
	memcpy(dst, src, BYTES_COUNT);
	crono = clock() - crono;
	printf("  - C: %ld ticks\n", crono);

	crono = clock();
	Memory_Copy_SSE2(dst, src, BYTES_COUNT);
	crono = clock() - crono;
	printf("  - C: %ld ticks\n", crono);

	_aligned_free(dst);
	_aligned_free(src);
}

// Matrix4x4

void LoadIdentity(float *matrix)
{
	matrix[ 0] = 1.0f; matrix[ 4] = 0.0f; matrix[ 8] = 0.0f; matrix[12] = 0.0f;
	matrix[ 1] = 0.0f; matrix[ 5] = 1.0f; matrix[ 9] = 0.0f; matrix[13] = 0.0f;
	matrix[ 2] = 0.0f; matrix[ 6] = 0.0f; matrix[10] = 1.0f; matrix[14] = 0.0f;
	matrix[ 3] = 0.0f; matrix[ 7] = 0.0f; matrix[11] = 0.0f; matrix[15] = 1.0f;
}

void LoadZero(float *matrix)
{
	matrix[ 0] = 0.0f; matrix[ 4] = 0.0f; matrix[ 8] = 0.0f; matrix[12] = 0.0f;
	matrix[ 1] = 0.0f; matrix[ 5] = 0.0f; matrix[ 9] = 0.0f; matrix[13] = 0.0f;
	matrix[ 2] = 0.0f; matrix[ 6] = 0.0f; matrix[10] = 0.0f; matrix[14] = 0.0f;
	matrix[ 3] = 0.0f; matrix[ 7] = 0.0f; matrix[11] = 0.0f; matrix[15] = 0.0f;
}

void LoadRandom(float *matrix)
{
	for (int i = 0; i < 16; i++)
		matrix[i] = (float)rand() / (float)RAND_MAX;
}

bool CompareMatrices(float *m1, float *m2)
{
	const float TOLLERANCE = FLT_EPSILON * 4;

	for (int i = 0; i < 16; i++) {
		if (fabsf(m1[i] - m2[i]) >= TOLLERANCE)
			return (false);
	}

	return (true);
}

void TestMatrix4x4Correctness()
{
	float m1[16], m2[16], r0[16], r[16];

	m1[ 0] = 7.0f; m1[ 4] = 2.0f; m1[ 8] = 1.0f; m1[12] = 0.3f;
	m1[ 1] = 8.1f; m1[ 5] = 1.0f; m1[ 9] = 0.1f; m1[13] = 1.0f;
	m1[ 2] = 9.0f; m1[ 6] = 3.5f; m1[10] = 1.0f; m1[14] = -1.0f;
	m1[ 3] = 7.0f; m1[ 7] = 4.0f; m1[11] = 0.2f; m1[15] = 1.0f;

	LoadIdentity(m2);
	LoadZero(r);

	Matrix4x4_Multiply_Matrix4x4_SSE(r, m1, m2);

	LoadRandom(m1);
	LoadRandom(m2);
	LoadZero(r);

	Matrix4x4_Multiply_Matrix4x4(r0, m1, m2);

	Matrix4x4_Multiply_Matrix4x4_SSE(r, m1, m2);
	if (CompareMatrices(r, r0) == false)
		throw exception("Matrix4x4_MultiplyOf_SSE not correct!");
}

void TestMatrix4x4Performances()
{
	const unsigned int MATRIX_COUNT = 1024 * 1024 * 2;

	clock_t crono;
	float *r0 = (float*)malloc(16 * sizeof(float) * MATRIX_COUNT);
	float *m1 = (float*)malloc(16 * sizeof(float) * MATRIX_COUNT);
	float *m2 = (float*)malloc(16 * sizeof(float) * MATRIX_COUNT);

	printf(" === Matrix4x4_Multiply\n");
	printf("   * Number of matrices: %d\n", MATRIX_COUNT);

	for (int i = 0; i < MATRIX_COUNT; i++) {
		LoadIdentity(m1 + i * 16);
		LoadIdentity(m2 + i * 16);
		LoadZero(r0 + i * 16);
	}

	crono = clock();
	for (int i = 0; i < MATRIX_COUNT; i++)
		Matrix4x4_Multiply_Matrix4x4(r0 + i * 16, m1 + i * 16, m2 + i * 16);
	crono = clock() - crono;
	printf("  - C: %ld ticks\n", crono);

	crono = clock();
	for (int i = 0; i < MATRIX_COUNT; i++)
		Matrix4x4_Multiply_Matrix4x4_SSE(r0 + i * 16, m1 + i * 16, m2 + i * 16);
	crono = clock() - crono;
	printf("  - C: %ld ticks\n", crono);

	free(r0);
	free(m1);
	free(m2);
}